package com.zp.demo.repush;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zp.demo.repush.anno.RePushAnno;
import com.zp.demo.repush.anno.RePushCutAnno;
import com.zp.demo.repush.anno.RePushSuccessOnceAnno;
import com.zp.demo.repush.exception.RePushException;
import com.zp.demo.repush.service.func.IsSucessFunc;
import com.zp.demo.repush.service.func.ReSubmitResultFunc;
import com.zp.demo.repush.service.func.StrategyKeyFunc;
import com.zp.demo.repush.vo.BaseRePushVo;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;

public class RePushUtils {
    /**
     * 抛出异常
     *
     * @param args
     * @param exceptionKey
     * @param message
     */
    public static void throwException(Object[] args, String exceptionKey, String message,IsSucessFunc isSucessFunc) {
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        StackTraceElement preStack = stackTraceElements[2];
        // 默认取上一个 栈空间
        BaseRePushVo rePushVo = getThreadInfo(preStack, exceptionKey, message, args);
        rePushVo.setIsSucessFunc(isSucessFunc);
        rePushVo.setIsSuccessClass(isSucessFunc.getClass().getName());
        throw new RePushException(rePushVo);
    }
    /**
     * 通过反射获取 相关信息
     *
     * @param stackTraceElement
     * @param exceptionKey
     * @param message
     * @param args
     * @return
     */
    public static BaseRePushVo getThreadInfo(StackTraceElement stackTraceElement, String exceptionKey, String message, Object[] args) {
        BaseRePushVo rePushVo = new BaseRePushVo(exceptionKey, args, message);
        // 方法信息
        String clazzName = stackTraceElement.getClassName();
        // 反射获取 对象信息
        Class<?> clazz = null;
        try {
            clazz = Class.forName(clazzName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String methodName = stackTraceElement.getMethodName();
        String methodId = clazzName;
        // 获取当前方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (methodName.equals(method.getName())) {
                methodId = getMethodId(clazz,method);
                if (method.isAnnotationPresent(RePushAnno.class)) {
                    RePushAnno reqMapping1 = method.getAnnotation(RePushAnno.class);
                    setAnno2Vo(rePushVo, reqMapping1);
                }
                break;
            }
        }
        rePushVo.setMethodId(methodId);
        rePushVo.setMethodName(methodName);
        rePushVo.setClazzName(clazzName);
        return rePushVo;
    }
    public static BaseRePushVo getBaseRePush4Aop( Class<?> clazz ,Method method,String exceptionKey, String message, Object[] args) {
        BaseRePushVo rePushVo = new BaseRePushVo(exceptionKey, args, message);
        // 反射获取 对象信息
        String clazzName = clazz.getName();
        String methodId = clazzName;
        // 获取当前方法
        methodId = getMethodId(clazz, method);
        if (method.isAnnotationPresent(RePushCutAnno.class)) {
            RePushCutAnno reqMapping1 = method.getAnnotation(RePushCutAnno.class);
            setAnno2Vo(rePushVo, reqMapping1);
        }
        rePushVo.setMethodId(methodId);
        rePushVo.setMethodName(method.getName());
        rePushVo.setClazzName(clazzName);
        return rePushVo;
    }
    private static void setAnno2Vo(BaseRePushVo rePushVo, RePushAnno rePushAnno) {
        if (rePushAnno.desc() != null && !"".equals(rePushAnno.desc())) {
            rePushVo.setDesc(rePushAnno.desc());
        }
        if (rePushAnno.app() != null && !"".equals(rePushAnno.app())) {
            rePushVo.setApp(rePushAnno.app());
        }
    }
    private static void setAnno2Vo(BaseRePushVo rePushVo, RePushCutAnno rePushAnno) {
        if (rePushAnno.desc() != null && !"".equals(rePushAnno.desc())) {
            rePushVo.setDesc(rePushAnno.desc());
        }
        if (rePushAnno.app() != null && !"".equals(rePushAnno.app())) {
            rePushVo.setApp(rePushAnno.app());
        }

        try {
            StrategyKeyFunc strategyKeyFunc = rePushAnno.strategyKey().newInstance();
            IsSucessFunc isSucessFunc = rePushAnno.isOkClazz().newInstance();
            if(rePushAnno.onlyOnce()){
                ReSubmitResultFunc reSubmitResultFunc = rePushAnno.reSubmitResult().newInstance();
                rePushVo.setReSubmitResultFunc(reSubmitResultFunc);
                rePushVo.setReSubmitResultClass(rePushAnno.reSubmitResult().getName());
            }
            rePushVo.setIsSuccessClass(rePushAnno.isOkClazz().getName());
            rePushVo.setIsSucessFunc(isSucessFunc);
            rePushVo.setStrategyKeyFunc(strategyKeyFunc);
            rePushVo.setStrategyKeyFuncClass(rePushAnno.strategyKey().getName());

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    //写一个md5加密的方法
    public static String generKey(BaseRePushVo baseRePushVo) {
        return generKey(baseRePushVo.getMethodId(),baseRePushVo.getArgs());
    }

    /**
     * 获取methodId
     * @param clazz
     * @param method
     * @return
     */
    public static String getMethodId(Class<?> clazz,Method method) {
        String methodId = clazz.getName();
        // 获取注解
        if (clazz.isAnnotationPresent(RePushAnno.class)) {
            RePushAnno rePushAnno = clazz.getAnnotation(RePushAnno.class);
            if (rePushAnno.value() != null && !"".equals(rePushAnno.value())) {
                methodId = rePushAnno.value();
            }
        }
       return getMMId(clazz,methodId,method);
        // 获取当前方法
    }
    public static String getMMId(Class<?> clazz ,String methodId,Method method){
        String methodVal = "";
        if (method.isAnnotationPresent(RePushAnno.class)) {
            RePushAnno rePushAnno = method.getAnnotation(RePushAnno.class);
            methodVal = getMethodVal(rePushAnno.value(),method);
        }
        if(method.isAnnotationPresent(RePushCutAnno.class)){
            RePushCutAnno rePushCutAnno = method.getAnnotation(RePushCutAnno.class);
            methodVal = getMethodVal(rePushCutAnno.value(),method);
        }
        return methodId + "." + methodVal;
    }
    public static String getMethodVal(String value,Method method){
        String methodVal = "";
        String methodPath = value;
        if (methodPath != null && !"".equals(methodPath)) {
            methodVal = methodPath;
        }else{
            methodVal = method.getName();
        }
        return methodVal;
    }

    /**
     * 唯一key
     * @param methodId
     * @param params
     * @return
     */
    public static String generKey(String methodId,Object[] params) {
        String arg = "";
        ObjectMapper mapper = new ObjectMapper();
        if(params != null){
            arg = Arrays.stream(params).map(x -> {
                try {
                    return mapper.writeValueAsString(x);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                return "";
            }).collect(Collectors.joining(","));
        }
        arg = DigestUtils.md5DigestAsHex(arg.getBytes());
        String value =  methodId + ":" +arg;
        return value;
    }

    /**
     * 判断结果
     * @param result
     * @param baseRePushVo
     * @return
     */
    public static boolean isSuccess( Object result,BaseRePushVo baseRePushVo ) {
        IsSucessFunc isSucessFunc = baseRePushVo.getIsSucessFunc();
        // 结果
        if (isSucessFunc == null) {
            Class successClazz = null;
            try {
                Assert.notNull(baseRePushVo.getIsSuccessClass(),"IsSuccessClass 或 IsSucessFunc 未定义");
                successClazz = Class.forName(baseRePushVo.getIsSuccessClass());
                try {
                    isSucessFunc = (IsSucessFunc) successClazz.newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return isSucessFunc.isSucess(result);
    }
    public static void main(String[] args) {
        BaseRePushVo pushVo = new BaseRePushVo();
        pushVo.setMethodId("aaaabababab");
        pushVo.setType("ababababaa");
        pushVo.setApp("bbbbbbbbb");
        System.out.println(generKey(pushVo));
    }
}
